package plus.lgx.textsearcher.searcher;

import plus.lgx.textsearcher.config.SearcherConfig;
import plus.lgx.textsearcher.searcher.extender.ExtenderEnum;
import plus.lgx.textsearcher.util.FileUtil;
import plus.lgx.textsearcher.util.Throws;

import java.io.File;
import java.text.ParseException;
import java.util.Comparator;
import java.util.Locale;
import java.util.regex.Pattern;

import static plus.lgx.textsearcher.config.SearcherConfig.DefaultSearcherConfig.*;
import static plus.lgx.textsearcher.constant.Const.*;
import static plus.lgx.textsearcher.util.PathUtil.standardize;

/**
 * Class name: Initializer
 *
 * Create time: 12/14/22 6:49 PM
 *
 * @author lgx
 * @version 1.0
 */
public final class Initializer {

    public static SearcherConfig init(String[] args) {
        if (args.length == 0) {
            System.out.println(HELP);
            System.exit(0);
        }

        Integer showSkipped = null;
        Integer mec = null;
        ExtenderEnum extenderEnum = null;
        Integer lines = null;
        Integer bufferSize = null;
        Pattern whitelist = null;
        Pattern blacklist = null;
        char[][] DELIMITER = new char[3][];
        Boolean randomize = null;
        String outputFileName = null;
        String sdp = null;
        String template = null;
        String searchContent0 = null;
        String searchContent = null;
        Comparator<File> sort = null;
        String skip = null;
        boolean descant = false;
        boolean server = SERVER;
        boolean console = CONSOLE;
        boolean noPause = NO_PAUSE;
        boolean html = HTML;
        boolean ignoreCase = IGNORE_CASE;
        int port = PORT;
        int maxFileCount = MAX_FILE_COUNT;

        for (String s : args)
            if (!s.startsWith("-"))
                searchContent0 = s;

        String sortValue = null;
        for (String s : args) {
            if (!s.startsWith("-")) {
                continue;
            }
            String[] arg = splitByEq(s);
            if (arg.length == 1) {
                switch (arg[0].toLowerCase(Locale.ROOT)) {
                    case "--help", "-?" -> {
                        System.out.println(HELP);
                        System.exit(0);
                    }
                    case "--version" -> {
                        System.out.println(VERSION);
                        System.exit(0);
                    }
                    case "--descant" -> {
                        descant = true;
                        continue;
                    }
//                    case "--server" -> {
//                        server = true;
//                        continue;
//                    }
                    case "--console" -> {
                        console = true;
                        continue;
                    }
                    case "--no-pause" -> {
                        noPause = true;
                        continue;
                    }
                    case "--html" -> {
                        html = true;
                        continue;
                    }
                    case "--ignore-case" -> {
                        ignoreCase = true;
                        continue;
                    }
                    default -> {
                    }
                }
            }
            if (arg.length == 2) {
                switch (arg[0].toLowerCase(Locale.ROOT)) {
                    case "--extend-method" -> {
                        String arg1 = arg[1].toLowerCase(Locale.ROOT);
                        switch (arg1) {
                            case "greedy":
                                extenderEnum = ExtenderEnum.GREEDY;
                                continue;
                            case "line":
                                extenderEnum = ExtenderEnum.LINE_;
                                continue;
                            case "max":
                                extenderEnum = ExtenderEnum.MAX;
                                continue;
                            default:
                                if (arg1.startsWith("multiline")) {
                                    extenderEnum = ExtenderEnum.MULTILINE_;
                                    String linesString = arg1.substring(9);
                                    if (linesString.isEmpty()) {
                                        lines = LINES;
                                    } else {
                                        lines = Throws.of(Integer::valueOf, linesString, LINES)
                                                .orElseErr("Line number must be an integer," +
                                                        " but got \"%s\", fell to default %d.%n", linesString, LINES);
                                    }
                                    if (lines < 0) {
                                        System.err.format("Line number must be a positive integer,"
                                                + " but got \"%s\", fell to default %d.%n", linesString, LINES);
                                        lines = LINES;
                                    }
                                } else {
                                    System.err.format("Unrecognized extend method: \"%s\", "
                                            + "fell to default method.%n", arg[1]);
                                }
                            case "default":
                                continue;
                        }
                    }
//                    case "--port" -> {
//                        port = Throws.autoPrintErr(Integer::valueOf, arg[1], (Integer) null,
//                                "port must be an integer, but got \"%s\"", arg[1]);
//                        continue;
//                    }
                    case "--max-extend-chars-count" -> {
                        mec = Throws.autoPrintErr(Integer::parseInt, arg[1],
                                mec, "Max extend chars count must be a positive integer, "
                                        + "but got \"%s\", fell to default value %d.%n", arg[1],
                                MAX_EXTEND_CHARS_COUNT);
                        continue;
                    }
                    case "--delimiter" -> {
                        DELIMITER[0] = arg[1].toCharArray();
                        continue;
                    }
                    case "--left-brackets" -> {
                        DELIMITER[1] = arg[1].toCharArray();
                        continue;
                    }
                    case "--right-brackets" -> {
                        DELIMITER[2] = arg[1].toCharArray();
                        continue;
                    }
                    case "--search-dir" -> {
                        sdp = standardize(arg[1], true);
                        continue;
                    }
                    case "--output" -> {
                        outputFileName = standardize(arg[1], false);
                        continue;
                    }
                    case "--search-text" -> {
                        if (searchContent0 == null) {
                            searchContent = arg[1];
                        } else {
                            System.err.format("Text is specified (%s), "
                                    + "\"--search-text\" option is ignored.%n", searchContent0);
                        }
                        continue;
                    }
                    case "--show-skipped" -> {
                        showSkipped = Throws.autoPrintErr(Integer::valueOf, arg[1], (Integer) null,
                                "The value of show-skipped must be a positive integer or zero, " +
                                        "but got \"%s\", fell to default value %d.%n", arg[1], SHOW_SKIPPED);
                        if (showSkipped < 0) {
                            System.err.println("The value of show-skipped must be a positive integer or zero. ");
                            System.exit(4);
                        }
                        continue;
                    }
                    case "--buffer-size" -> {
                        try {
                            long fileLength = FileUtil.fromFileLength(arg[1]);
                            if (fileLength > Integer.MAX_VALUE) {
                                System.err.format("The given buffer size is too large, fell to default value %d.%n",
                                        BUFFER_SIZE);
                                bufferSize = BUFFER_SIZE;
                            } else if (fileLength < 256) {
                                System.err.format("The given buffer size is too small, fell to default value %d.%n",
                                        BUFFER_SIZE);
                                bufferSize = BUFFER_SIZE;
                            } else {
                                bufferSize = (int) fileLength;
                            }
                        } catch (ParseException ex) {
                            System.err.format("Parse buffer size error, fell to default value %d.%n", BUFFER_SIZE);
                            bufferSize = BUFFER_SIZE;
                        }
                        continue;
                    }
                    case "--whitelist" -> {
                        try {
                            whitelist = Pattern.compile(arg[1]);
                        } catch (Exception e) {
                            System.err.println("regexp of whitelist compile error: " + e.getMessage());
                            System.exit(4);
                        }
                        continue;
                    }
                    case "--blacklist" -> {
                        try {
                            blacklist = Pattern.compile(arg[1]);
                        } catch (Exception e) {
                            System.err.println("regexp of blacklist compile error: " + e.getMessage());
                            System.exit(4);
                        }
                        continue;
                    }
                    case "--sort" -> {
                        sortValue = arg[1];
                        continue;
                    }
                    case "--template" -> {
                        template = standardize(arg[1], true);
                        continue;
                    }
                    case "--ignore" -> {
                        try {
                            Pattern.compile(arg[1]);
                        } catch (Exception e) {
                            System.err.format("Errors occur while compiling the regex" +
                                            " of the option of IGNORE \"%s\". %n", arg[1]);
                            System.exit(8);
                        }
                        skip = arg[1];
                        continue;
                    }
                    case "--max-file-count" -> {
                        try {
                            maxFileCount = Integer.parseInt(arg[1]);
                            if (maxFileCount < 1) throw new RuntimeException();
                            continue;
                        } catch (Exception e) {
                            System.err.format("Parse MAX FILE COUNT error, because it isn't a positive integer. ");
                            System.exit(9);
                        }
                    }
                    default -> {
                    }
                }
            }
            System.err.format("Skipped unrecognized option \"%s\".%n", s);
        }

        if (sortValue != null) {
            switch (sortValue.toLowerCase(Locale.ROOT)) {
                case "filename":
                    sort = Boolean.TRUE.equals(descant)
                            ? (f1, f2) -> f2.getName().compareToIgnoreCase(f1.getName())
                            : (f1, f2) -> f1.getName().compareToIgnoreCase(f2.getName());
                    break;
                case "filesize":
                    sort = Boolean.TRUE.equals(descant)
                            ? (f1, f2) -> (int) (f2.length() - f1.length())
                            : (f1, f2) -> (int) (f1.length() - f2.length());
                    break;
                case "randomize":
                    if (Boolean.TRUE.equals(descant))
                        System.out.println("Ignored unused option `descant` because sort method is randomize. ");
                    randomize = true;
                    break;
                default:
                    System.err.format("Unrecognized sort method \"%s\", fell to default \"%s\"%n", sortValue, SORT0);
                case "none":
                    if (Boolean.TRUE.equals(descant))
                        System.out.format("Ignored unused option `descant` because sort method is %s. ", SORT0);
            }
        }

        // check search content
        searchContent0 = searchContent0 == null ? searchContent : searchContent0;
        if (!server && searchContent0 == null) {
            System.out.println(HELP);
            System.exit(3);
        }
        if (!server && searchContent0.isBlank()) {
            System.err.println("The search text cannot be an empty string. ");
            System.exit(3);
        }
        if (searchContent0 == null || searchContent0.length() < 2) {
            skip = null;
        }

        return new SearcherConfig(
                showSkipped, mec,       extenderEnum,   lines,   bufferSize,     whitelist,  blacklist,
                DELIMITER,   randomize, outputFileName, sdp,     searchContent0, sort,       server,
                port,        template,  console,        noPause, html,           ignoreCase, skip,
                maxFileCount
        );
    }

    private static String[] splitByEq(String s) {
        int ind;
        if ((ind = s.indexOf('=')) == -1) return new String[]{s};
        return new String[]{s.substring(0, ind), s.substring(ind + 1)};
    }

}
